package com.lagou.edu.factory;

import com.lagou.edu.annotation.*;
import com.mysql.jdbc.StringUtils;
import org.reflections.Reflections;
import org.reflections.scanners.FieldAnnotationsScanner;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.MethodParameterScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ConfigurationBuilder;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;


public class AnnotationBeanFactory implements BeanFactory {

    private static AnnotationBeanFactory annotationBeanFactory = new AnnotationBeanFactory();

    private AnnotationBeanFactory() {
    }

    public static AnnotationBeanFactory createBeanFactory() {
        return annotationBeanFactory;
    }

    static {
        try {
            init();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化容器
     * 1.从classpath扫描全包的文件
     * <p/>
     * 2.将注解@service,@repository的类注册到容器中
     * <p/>
     * 3.将扫描到的@autowired对应的字段赋值
     * <p/>
     * 一.根据类型进行匹配对象,池的中存储<Class,Object>
     */
    private static void init() throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException, IntrospectionException {
        createBeanToPool();
        diFiledToBean();
        createTransactionalProxy();
    }

    private static void createTransactionalProxy() {
        //通过扫描@trancational注解确定需要代理的类,并生成代理对象替换容器中原对像
        List<Class<?>> transactionalClass = getAnnotationClass(Transactional.class);
        ProxyFactory proxyFactory = (ProxyFactory) beanIdPool.get(beanClassPool.get(ProxyFactory.class).get(0));
        for (Class<?> bClass : transactionalClass) {
            List<String> list = beanClassPool.get(bClass);
            for (String name : list) {
                Object o = beanIdPool.get(name);
                Object cglibProxy = proxyFactory.getCglibProxy(o);
                beanIdPool.put(name,cglibProxy);
            }
        }
    }

    /**
     * 注入@overwrite注解的字段到bean中
     *
     * @throws IllegalAccessException
     */
    private static void diFiledToBean() throws IllegalAccessException {
        //3.将扫描到的@autowired对应的字段赋值
        for (Map.Entry<String, Object> objectEntry : beanIdPool.entrySet()) {
            Object o1 = objectEntry.getValue();
            Set<Field> annotationField = getAnnotationField(o1.getClass(), AutoWired.class);
            for (Field field : annotationField) {
                Class<?> type = field.getType();
                //获取注入的名字
                AutoWired annotation = field.getAnnotation(AutoWired.class);
                field.setAccessible(true);
                String name = annotation.value();

                if (StringUtils.isEmptyOrWhitespaceOnly(name)) {
                    //使用类型注入
                    List<String> list = beanClassPool.get(type);

                    if (list.size() == 1) {
                        field.set(o1, beanIdPool.get(list.get(0)));
                    } else {
                        throw new RuntimeException(String.format("%s 无法注入 %s 因为 %s 不唯一或者不存在!", o1.getClass().getName(), type.getName(), type.getName()));
                    }

                } else {
                    Object o = beanIdPool.get(name);
                    field.set(o1, o);
                }
            }
        }
    }

    /**
     * 实例化类到容器中
     *
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     */
    private static void createBeanToPool() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //遍历所有的@service,@repository类,并实例化
        List<Class<?>> annotationClass = getAnnotationClass(Component.class);
        for (Class<?> aClass : annotationClass) {
            //实例化放入到容器中
            Class<?>[] interfaces = aClass.getInterfaces();
            //本类添加到beanClassPool
            Class<?>[] classAndInterfaces = new Class<?>[interfaces.length+1];
            for (int i = 0; i <interfaces.length; i++) {
                classAndInterfaces[i]=interfaces[i];
            }
            classAndInterfaces[interfaces.length]=aClass;

            Annotation[] annotations = aClass.getAnnotations();
            //默认为全类名
            String name = aClass.getName();
            //获取名称
            for (Annotation annotation : annotations) {
                if (annotation instanceof Service) {
                    String name1 = ((Service) annotation).value();
                    name = StringUtils.isEmptyOrWhitespaceOnly(name1) ? name : name1;
                } else if (annotation instanceof Repository) {
                    String name1 = ((Repository) annotation).value();
                    name = StringUtils.isEmptyOrWhitespaceOnly(name1) ? name : name1;
                }
            }
            Object o = aClass.getDeclaredConstructor().newInstance();
            //在id获取的容器中添加对象
            if (beanIdPool.containsKey(name)) {
                throw new RuntimeException(String.format("存在两个名字为 %s 的对象", name));
            } else {
                beanIdPool.put(name, o);
            }

            for (Class<?> anInterface : classAndInterfaces) {
                //在接口容器中添加对象
                if (beanClassPool.containsKey(anInterface)) {
                    beanClassPool.get(anInterface).add(name);
                } else {
                    LinkedList<String> objects = new LinkedList<>();
                    objects.add(name);
                    beanClassPool.put(anInterface,objects);
                }
            }

        }
    }


    /**
     * 获取指定文件下面的RequestMapping方法保存在mapp中
     *
     * @return
     */
    public static List<Class<?>> getAnnotationClass(Class<? extends Annotation>... annotationClass) {
        List<Class<?>> classesList = new LinkedList<>();
        for (Class<? extends Annotation> aClass : annotationClass) {
            Reflections reflections = new Reflections("");
            Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(aClass);
            //删除注解类
            typesAnnotatedWith.removeIf(aClass1 -> aClass1.isAnnotation());
            classesList.addAll(typesAnnotatedWith);
        }
        return classesList;
    }

    /**
     * 获取指定文件下面的RequestMapping方法保存在mapp中
     *
     * @return
     */
    public static Set<Field> getAnnotationField(Class<?> findClass, Class<? extends Annotation>... annotationClass) {
        Set<Field> filedList = new HashSet<>();
        for (Class<? extends Annotation> aClass : annotationClass) {
            Reflections reflections = new Reflections(findClass.getName(), new FieldAnnotationsScanner());
            Set<Field> fieldsAnnotatedWith = reflections.getFieldsAnnotatedWith(aClass);
            filedList.addAll(fieldsAnnotatedWith);
        }
        //查找父类中的字段
        if (findClass.getSuperclass() != null) {
            filedList.addAll(getAnnotationField(findClass.getSuperclass()));
        }

        return filedList;
    }

    @Override
    public <T> T getBean(String id) {
        return (T) beanIdPool.get(id);
    }

    @Override
    public <T> T getBean(Class<T> clazz) {
        return (T) beanIdPool.get(beanClassPool.get(clazz).get(0));
    }

}
